
#include "Parser.h"
#include "PinholeCamera.h"
#include "ConstantBackground.h"
#include "PointLight.h"
#include "LambertianMaterial.h"
#include "DielectricMaterial.h"
#include "PhongMaterial.h"
#include "PhongImageMaterial.h"
#include "PhongMarbleMaterial.h"
#include "MetalMaterial.h"
#include "MaterialMetalBumpy.h"
#include "TestFunctionMaterial.h"
#include "MaterialUVWDebug.h"
#include "MaterialPhongVolume.h"
#include "CheckerMaterial.h"
#include "Group.h"
#include "GroupGridded.h"
#include "Plane.h"
#include "Sphere.h"
#include "SpherePolar.h"
#include "Box.h"
#include "Heightfield.h"
#include "Ring.h"
#include "Disk.h"
#include "Triangle.h"
#include "Scene.h"
#include "Image.h"
#include "Sampler.h"
#include "SamplerUniform.h"
#include "SamplerJittered.h"

#include <cmath>
#include <iostream>
#include <istream>
#include <sstream>
#include <string>
#include <cstdlib>
using namespace std;

void Parser::throwParseException(
    string const &message ) const
{
  cerr << next_token.line_number << ":" << next_token.column_number << ": " << message << endl;
  exit( 1 );
}

void Parser::readNextToken()
{
  int state = 0;
  long long mantissa = 0;
  int exponent = 0;
  int exponent_adjustment = 0;
  bool negative_mantissa = false;
  bool negative_exponent = false;
  for ( ; ; ) {
    int character = input.get();
    switch ( state ) {
      case 0:
        next_token.line_number = line_number;
        next_token.column_number = column_number;
        if ( input.eof() ) {
          next_token.token_type = Token::end_of_file;
          return;
        } else if ( character == ' ' || character == '\t' ||
                    character == '\r' || character == '\n' )
          state = 0;
        else if ( character == '/' )
          state = 1;
        else if ( character == '+' || character == '-' ) {
          negative_mantissa = character == '-';
          state = 3;
        } else if ( character >= '0' && character <= '9' ) {
          mantissa = character - '0';
          state = 4;
        } else if ( character == '.' )
          state = 5;
        else if ( character == '"' ) {
          next_token.string_value.clear();
          state = 10;
        } else if ( character >= 'A' && character <= 'Z' ||
                    character >= 'a' && character <= 'z' ||
                    character == '_' ) {
          next_token.string_value = static_cast< char >( character );
          state = 12;
        } else if ( character == ',' ) {
          ++column_number;
          next_token.token_type = Token::comma;
          return;
        } else if ( character == '{' ) {
          ++column_number;
          next_token.token_type = Token::left_brace;
          return;
        } else if ( character == '}' ) {
          ++column_number;
          next_token.token_type = Token::right_brace;
          return;
        } else if ( character == '[' ) {
          ++column_number;
          next_token.token_type = Token::left_bracket;
          return;
        } else if ( character == ']' ) {
          ++column_number;
          next_token.token_type = Token::right_bracket;
          return;
        } else
          throwParseException( "Unexpected character" );
        break;
      case 1:
        if ( character == '/' )
          state = 2;
        else
          throwParseException( "Malformed comment" );
        break;
      case 2:
        if ( character == '\n' || input.eof() )
          state = 0;
        break;
      case 3:
        if ( character >= '0' && character <= '9' ) {
          mantissa = character - '0';
          state = 4;
        } else if ( character == '.' )
          state = 5;
        else
          throwParseException( "Invalid number" );
        break;
      case 4:
        if ( character >= '0' && character <= '9' )
          mantissa = mantissa * 10 + character - '0';
        else if ( character == '.' )
          state = 6;
        else if ( character == 'E' || character == 'e' )
          state = 7;
        else {
          input.putback( static_cast< char >( character ) );
          next_token.integer_value = ( static_cast< int >( mantissa ) *
                                       ( negative_mantissa ? -1 : 1 ) );
          next_token.token_type = Token::integer;
          return;
        }
        break;
      case 5:
        if ( character >= '0' && character <= '9' ) {
          mantissa = character - '0';
          exponent_adjustment = 1;
          state = 6;
        } else
          throwParseException( "Invalid number" );
        break;
      case 6:
        if ( character >= '0' && character <= '9' ) {
          mantissa = mantissa * 10 + character - '0';
          ++exponent_adjustment;
        } else if ( character == 'E' || character == 'e' )
          state = 7;
        else {
          input.putback( static_cast< char >( character ) );
          next_token.real_value = ( mantissa * ( negative_mantissa ? -1 : 1 ) *
                                    pow( 10.0, -exponent_adjustment ) );
          next_token.token_type = Token::real;
          return;
        }
        break;
      case 7:
        if ( character == '+' || character == '-' ) {
          negative_exponent = character == '-';
          state = 8;
        } else if ( character >= '0' && character <= '9' ) {
          exponent = character - '0';
          state = 9;
        } else
          throwParseException( "Invalid number" );
        break;
      case 8:
        if ( character >= '0' && character <= '9' ) {
          exponent = character - '0';
          state = 9;
        } else
          throwParseException( "Invalid number" );
        break;
      case 9:
        if ( character >= '0' && character <= '9' )
          exponent = exponent * 10 + character - '0';
        else {
          input.putback( static_cast< char >( character ) );
          next_token.real_value = ( mantissa * ( negative_mantissa ? -1 : 1 ) *
                                    pow( 10.0, ( exponent * ( negative_exponent ? -1 : 1 ) -
                                                 exponent_adjustment ) ) );
          next_token.token_type = Token::real;
          return;
        }
        break;
      case 10:
        if ( input.eof() || character == '\n' )
          throwParseException( "Unterminated string" );
        else if ( character == '\\' )
          state = 11;
        else if ( character == '"' ) {
          ++column_number;
          next_token.token_type = Token::string;
          return;
        } else
          next_token.string_value.push_back( static_cast< char >( character ) );
        break;
      case 11:
        if ( input.eof() )
          throwParseException( "Unterminated string" );
        else if ( character == '\n' )
          next_token.string_value.push_back( '\n' );
        else if ( character == '\\' )
          next_token.string_value.push_back( '\\' );
        else if ( character == '"' )
          next_token.string_value.push_back( '"' );
        else
          next_token.string_value.push_back( static_cast< char >( character ) );
        state = 10;
        break;
      case 12:
        if ( character >= '0' && character <= '9' ||
             character >= 'A' && character <= 'Z' ||
             character >= 'a' && character <= 'z' ||
             character == '_' )
          next_token.string_value.push_back( static_cast< char >( character ) );
        else {
          input.putback( static_cast< char >( character ) );
          next_token.token_type = Token::string;
          return;
        }
        break;
    }
    if ( character == '\n' )
    {
      ++line_number;
      column_number = 0;
    }
    else if ( character == '\t' )
      column_number = ( column_number + 8 ) / 8 * 8;
    else
      ++column_number;
  }
}

bool Parser::peek(
  Token::type const type )
{
  bool matched = next_token.token_type == type;
  if ( matched )
    readNextToken();
  return matched;
}

bool Parser::peek(
  string const &keyword )
{
  bool matched = ( next_token.token_type == Token::string &&
                   next_token.string_value == keyword );
  if ( matched )
    readNextToken();
  return matched;
}

Parser::Token Parser::match(
  Token::type const type,
  string const &failure_message )
{
  if ( next_token.token_type != type )
    throwParseException( failure_message );
  Token current_token( next_token );
  readNextToken();
  return current_token;
}

Parser::Token Parser::match(
  string const &keyword,
  string const &failure_message )
{
  if ( next_token.token_type != Token::string ||
       next_token.string_value != keyword )
    throwParseException( failure_message );
  Token current_token( next_token );
  readNextToken();
  return current_token;
}

string Parser::parseString()
{
  Token next( match( Token::string, "Expected a string" ) );
  return next.string_value;
}

bool Parser::parseBoolean()
{
  if ( peek( "true" ) )
    return true;
  else if ( peek( "false" ) )
    return false;
  else
    throwParseException( "Expected `true' or `false'." );
  return false;
}

int Parser::parseInteger()
{
  Token next( match( Token::integer, "Expected an integer" ) );
  return next.integer_value;
}

double Parser::parseReal()
{
  if ( next_token.token_type == Token::integer ) {
    Token next( match( Token::integer, "Expected an integer or real" ) );
    return static_cast< double >( next.integer_value );
  }
  Token next( match( Token::real, "Expected an integer or real" ) );
  return next.real_value;
}

Vector const Parser::parseVector()
{
  match( Token::left_bracket, "Expected a left bracket" );
  double x = parseReal();
  match( Token::comma, "Expected a comma" );
  double y = parseReal();
  match( Token::comma, "Expected a comma" );
  double z = parseReal();
  match( Token::right_bracket, "Expected a right bracket" );
  return Vector( x, y, z );
}

Point const Parser::parsePoint()
{
  match( Token::left_bracket, "Expected a left bracket" );
  double x = parseReal();
  match( Token::comma, "Expected a comma" );
  double y = parseReal();
  match( Token::comma, "Expected a comma" );
  double z = parseReal();
  match( Token::right_bracket, "Expected a right bracket" );
  return Point( x, y, z );
}

Color const Parser::parseColor()
{
  if ( peek( Token::left_bracket ) ) {
    double r = parseReal();
    match( Token::comma, "Expected a comma" );
    double g = parseReal();
    match( Token::comma, "Expected a comma" );
    double b = parseReal();
    match( Token::right_bracket, "Expected a right bracket" );
    return Color( r, g, b );
  }
  double v = parseReal();
  return Color( v, v, v );
}

Camera *Parser::parsePinholeCamera()
{
  Point eye( 0.0, 0.0, 0.0 );
  Point lookat( 0.0, 1.0, 0.0 );
  Vector up( 0.0, 0.0, 1.0 );
  double hfov = 90.0;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "eye" ) )
        eye = parsePoint();
      else if ( peek( "lookat" ) )
        lookat = parsePoint();
      else if ( peek( "up" ) )
        up = parseVector();
      else if ( peek( "hfov" ) )
        hfov = parseReal();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `eye', `lookat', `up', `hfov' or }." );
    }
  return new PinholeCamera( eye, lookat, up, hfov );
}

Camera *Parser::parseCamera()
{
    if ( peek( "pinhole" ) )
        return parsePinholeCamera();
    throwParseException( "Expected a camera type." );
    return 0;
}

Background *Parser::parseConstantBackground()
{
  Color color( 0.0, 0.0, 0.0 );
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "color" ) )
        color = parseColor();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `color' or }." );
    }
  return new ConstantBackground( color );
}

Background *Parser::parseBackground()
{
    if ( peek( "constant" ) )
        return parseConstantBackground();
    throwParseException( "Expected a background type." );
    return 0;
}

Light *Parser::parsePointLight()
{
  Point position( 0.0, 0.0, 10.0 );
  Color color( 1.0, 1.0, 1.0 );
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "position" ) )
        position = parsePoint();
      else if ( peek( "color" ) )
        color = parseColor();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `position', `color' or }." );
    }
  return new PointLight( position, color );
}

Light *Parser::parseLight()
{
    if ( peek( "point" ) )
      return parsePointLight();
    throwParseException( "Expected a light type." );
    return 0;
}


Material *Parser::parseLambertianMaterial()
{
  Color color( 1.0, 1.0, 1.0 );
  double Kd = 0.6;
  double Ka = 0.3;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "color" ) )
        color = parseColor();
      else if ( peek( "Kd" ) )
        Kd = parseReal();
      else if ( peek( "Ka" ) )
        Ka = parseReal();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `color', `Kd', `Ka' or }." );
    }
  return new LambertianMaterial( color, Kd, Ka );
}

Material *Parser::parsePhongMaterial()
{
  Color color( 1.0, 1.0, 1.0 );
  double Kd = 0.6;
  double Ka = 0.3;
  Color highlight;
  double exponent = 1.0;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "color" ) )
        color = parseColor();
      else if ( peek( "Kd" ) )
        Kd = parseReal();
      else if ( peek( "Ka" ) )
        Ka = parseReal();
      else if ( peek( "highlight" ) )
    	highlight = parseColor();
      else if ( peek( "exponent" ) )
    	exponent = parseReal();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected 'color', 'Kd', 'Ka', 'highlight', 'exponent' or }." );
    }
  return new PhongMaterial( color, Kd, Ka, highlight, exponent );
}

Material *Parser::parsePhongImageMaterial()
{
	  string file;
	  double Kd = 0.6;
	  double Ka = 0.3;
	  Color highlight;
	  double exponent = 1.0;
	  if ( peek( Token::left_brace ) )
	    for ( ; ; )
	    {
	      if ( peek( "file" ) )
	        file = parseString();
	      else if ( peek( "Kd" ) )
	        Kd = parseReal();
	      else if ( peek( "Ka" ) )
	        Ka = parseReal();
	      else if ( peek( "highlight" ) )
	    	highlight = parseColor();
	      else if ( peek( "exponent" ) )
	    	exponent = parseReal();
	      else if ( peek( Token::right_brace ) )
	        break;
	      else
	        throwParseException( "Expected 'file', 'Kd', 'Ka', 'highlight', 'exponent' or }." );
	    }
	  return new PhongImageMaterial( file, Kd, Ka, highlight, exponent );
}

Material *Parser::parseMaterialPhongVolume()
{
	  string header_file;
	  string cmap_file;
	  Point lower;
	  Point upper;
	  double stepsize;
	  float maxopacity;
	  double Kd = 0.6;
	  double Ka = 0.3;
	  Color highlight;
	  double exponent = 1.0;
	  if ( peek( Token::left_brace ) )
	    for ( ; ; )
	    {
	      if ( peek( "header" ) )
	        header_file = parseString();
	      else if ( peek( "cmapfile" ) )
	      	cmap_file = parseString();
	      else if ( peek( "upper" ) )
	    	upper = parsePoint();
	      else if ( peek( "lower" ) )
	    	lower = parsePoint();
	      else if ( peek( "stepsize" ) )
	    	stepsize = parseReal();
	      else if ( peek( "maxopacity" ) ) 
	    	maxopacity = parseReal();
	      else if ( peek( "Kd" ) )
	        Kd = parseReal();
	      else if ( peek( "Ka" ) )
	        Ka = parseReal();
	      else if ( peek( "highlight" ) )
	    	highlight = parseColor();
	      else if ( peek( "exponent" ) )
	    	exponent = parseReal();
	      else if ( peek( Token::right_brace ) )
	        break;
	      else
	        throwParseException( "Expected 'header', 'cmapfile', 'upper', 'lower', 'stepsize', 'maxopacity', 'Kd', 'Ka', 'highlight', 'exponent' or } in phongvolume declaration." );
	    }
	  return new MaterialPhongVolume( header_file, cmap_file, lower, upper, stepsize, maxopacity, Kd, Ka, highlight, exponent );
}

Material *Parser::parsePhongMarbleMaterial()
{
	  string file;
	  double Kd = 0.6;
	  double Ka = 0.3;
	  double tscale;
	  double fscale;
	  double lacunarity;
	  double gain;
	  double scale;
	  int octaves;
	  Color highlight;
	  Color color1;
	  Color color2;
	  double exponent = 1.0;
	  if ( peek( Token::left_brace ) )
	    for ( ; ; )
	    {
	      if ( peek( "Kd" ) )
	        Kd = parseReal();
	      else if ( peek( "Ka" ) )
	        Ka = parseReal();
	      else if ( peek( "tscale" ) )
	      	tscale = parseReal();
	      else if ( peek( "fscale" ) )
	      	fscale = parseReal();
	      else if ( peek( "lacunarity" ) )
	      	lacunarity = parseReal();
	      else if ( peek( "gain" ) )
	      	gain = parseReal();
	      else if ( peek( "scale" ) )
	      	scale = parseReal();
	      else if ( peek( "octaves" ) )
	      	octaves = parseInteger();
	      else if ( peek( "color1" ) )
	      	color1 = parseColor();	      	      
	      else if ( peek( "color2" ) )
	      	color2 = parseColor();	      	      
	      else if ( peek( "highlight" ) )
	    	highlight = parseColor();
	      else if ( peek( "exponent" ) )
	    	exponent = parseReal();
	      else if ( peek( Token::right_brace ) )
	        break;
	      else
	        throwParseException( "Expected 'Kd', 'Ka', 'tscale', 'fscale', 'lacunarity', 'gain', scale', 'octaves', 'color1', 'color2', 'highlight', 'exponent' or }." );
	    }
	  return new PhongMarbleMaterial( color1, color2, scale, octaves, tscale, fscale, lacunarity, gain, Kd, Ka, highlight, exponent);
}

Material *Parser::parseDielectricMaterial()
{
  double eta = 1.0;
  double exponent = 100;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "eta" ) )
        eta = parseReal();
      else if ( peek( "exponent" ) )
        exponent = parseReal();
      else if ( peek( Token::right_brace ) )
    	break;
      else
        throwParseException( "Expected 'eta' or 'exponent' or }." );
    }
  return new DielectricMaterial( eta, exponent );
}

Material *Parser::parseMetalMaterial()
{
  Color color( 1.0, 1.0, 1.0 );
  double exponent = 1.0;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "color" ) )
        color = parseColor();
      else if ( peek( "exponent" ) )
        exponent = parseReal();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected 'color', 'exponent' or }." );
    }
  return new MetalMaterial( color, exponent );
}

Material *Parser::parseMaterialMetalBumpy()
{
  Color color( 1.0, 1.0, 1.0 );
  double exponent = 1.0;
  double bumpiness = 0.1;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "color" ) )
        color = parseColor();
      else if ( peek( "exponent" ) )
        exponent = parseReal();
      else if ( peek( "bumpiness" ) )
    	bumpiness = parseReal();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected 'color', 'exponent' or }." );
    }
  return new MaterialMetalBumpy( color, exponent, bumpiness );
}

Material *Parser::parseMaterialUVWDebug()
{
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected '{ }'." );
    }
  return new MaterialUVWDebug( );
}

Material *Parser::parseTestFunctionMaterial()
{
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected 'color', 'exponent' or }." );
    }
  return new TestFunctionMaterial( );
}

Material *Parser::parseCheckerMaterial()
{
	Point origin;
	Vector vector1;
	Vector vector2;
	Material* material1;
	Material* material2;
	
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( Token::right_brace ) )
        break;
      else if ( peek( "origin" ) )
    	  origin = parsePoint();
      else if ( peek( "vector1" ) )
    	  vector1 = parseVector();
      else if ( peek( "vector2" ) )
    	  vector2 = parseVector();
      else if ( peek( "material1" ) )
    	  material1 = parseMaterial();
      else if ( peek( "material2" ) )
    	  material2 = parseMaterial();
      else
        throwParseException( "Expected 'origin', 'vector1', 'vector2', 'material1', 'material2', or }." );
    }
  return new CheckerMaterial( origin, vector1, vector2, material1, material2 );
}

Material *Parser::parseMaterial()
{
    if ( peek( "lambertian" ) )
      return parseLambertianMaterial();
    else if ( peek( "phong" ) )
    	return parsePhongMaterial();
    else if ( peek( "phongimage" ) )
    	return parsePhongImageMaterial();
    else if ( peek( "phongmarble" ) )
    	return parsePhongMarbleMaterial();
    else if ( peek( "phongvolume" ) )
    	return parseMaterialPhongVolume();
    else if ( peek( "dielectric" ) )
    	return parseDielectricMaterial();
    else if ( peek( "checker" ) )
    	return parseCheckerMaterial();
    else if ( peek( "metal" ) )
    	return parseMetalMaterial();
    else if ( peek( "metalbumpy" ) )
    	return parseMaterialMetalBumpy();
    else if ( peek( "testfunction" ) )
    	return parseTestFunctionMaterial();
    else if ( peek( "uvwdebug" ) )
        return parseMaterialUVWDebug();
    else if ( next_token.token_type == Token::string )
    {
        map< string, Material * >::iterator found = defined_materials.find( parseString() );
        if ( found != defined_materials.end() )
            return ( *found ).second;
    }
    throwParseException( "Expected an material type (phong, metal, checker, etc)." );
    return 0;
}

Object *Parser::parseGroupObject()
{
    Group *group = new Group();
    match( Token::left_brace, "Expected a left brace" );
    while ( !peek( Token::right_brace ) )
        group->addObject( parseObject() );
    return group;
}

Object *Parser::parseGroupGriddedObject()
{
	double cellfactor;
	match( Token::left_brace, "Expected left brace to begin griddedgroup");
	if ( peek( "cellfactor" ) )
	    cellfactor = parseReal();
	else
		throwParseException(" Expected 'cellfactor <num>' immediately after griddedgroup");
	GroupGridded *group = new GroupGridded(cellfactor);
    while ( !peek( Token::right_brace ) ) {
    	group->addObject( parseObject() );
    }
    return group;
}

Object *Parser::parsePlaneObject()
{
  Material *material = default_material;
  Vector normal( 0.0, 0.0, 1.0 );
  Point point( 0.0, 0.0, 0.0 );
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "material" ) )
        material = parseMaterial();
      else if ( peek( "normal" ) )
        normal = parseVector();
      else if ( peek( "point" ) )
        point = parsePoint();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `material', `point', `normal' or }." );
    }
  return new Plane( material, normal, point );
}

Object *Parser::parseSphereObject()
{
  Material *material = default_material;
  Point center( 0.0, 0.0, 0.0 );
  double radius = 0.5;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "material" ) )
        material = parseMaterial();
      else if ( peek( "center" ) )
        center = parsePoint();
      else if ( peek( "radius" ) )
        radius = parseReal();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `material', `center', `radius' or }." );
    }
  return new Sphere( material, center, radius );
}

Object *Parser::parseSpherePolarObject()
{
  Material *material = default_material;
  Point center( 0.0, 0.0, 0.0 );
  double radius = 0.5;
  Vector meridian(0, 0, 0);
  Vector pole(0, 0, 0);
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "material" ) )
        material = parseMaterial();
      else if ( peek( "center" ) )
        center = parsePoint();
      else if ( peek( "radius" ) )
        radius = parseReal();
      else if ( peek( "pole" ) )
        pole = parseVector();
      else if ( peek( "meridian" ) )
        meridian = parseVector();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `material', `center', `radius', 'pole', 'meridian', or }." );
    }
  return new SpherePolar( material, center, radius, pole, meridian );
}

Object *Parser::parseBoxObject()
{
  Material *material = default_material;
  Point corner1( 0.0, 0.0, 0.0 );
  Point corner2( 0.0, 0.0, 0.0 );
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "material" ) )
        material = parseMaterial();
      else if ( peek( "corner1" ) )
        corner1 = parsePoint();
      else if ( peek( "corner2" ) )
        corner2 = parsePoint();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `material', `corner1', `corner2' or }." );
    }
  return new Box( material, corner1, corner2 );
}

Object *Parser::parseHeightfieldObject()
{
  Material *material = default_material;
  Point corner1( 0.0, 0.0, 0.0 );
  Point corner2( 0.0, 0.0, 0.0 );
  string file;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "material" ) )
        material = parseMaterial();
      else if ( peek( "corner1" ) )
        corner1 = parsePoint();
      else if ( peek( "corner2" ) )
        corner2 = parsePoint();
      else if ( peek( "file" ) )
    	file = parseString();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `material', `corner1', `corner2', 'file' or }." );
    }
  return new Heightfield( material, corner1, corner2, file );
}

Object *Parser::parseDiskObject()
{
  Material *material = default_material;
  Point center( 0.0, 0.0, 0.0 );
  Vector normal( 0.0, 0.0, 0.0 );
  double radius = 0.5;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "material" ) )
        material = parseMaterial();
      else if ( peek( "center" ) )
        center = parsePoint();
      else if ( peek( "normal" ) )
        normal = parseVector();
      else if ( peek( "radius" ) )
        radius = parseReal();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `material', `center', `radius' or }." );
    }
  return new Disk( material, center, normal, radius );
}

Object *Parser::parseRingObject()
{
  Material *material = default_material;
  Point center( 0.0, 0.0, 0.0 );
  Vector normal( 0.0, 0.0, 0.0 );
  double radius1 = 0.5;
  double radius2 = 1.0;
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "material" ) )
        material = parseMaterial();
      else if ( peek( "center" ) )
        center = parsePoint();
      else if ( peek( "normal" ) )
        normal = parseVector();
      else if ( peek( "radius1" ) )
        radius1 = parseReal();
      else if ( peek( "radius2" ) )
        radius2 = parseReal();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `material', `center', `radius1', `radius2' or }." );
    }
  return new Ring( material, center, normal, radius1, radius2 );
}

Object *Parser::parseTriangleObject()
{
  Material *material = default_material;
  Point corner1( 0.0, 0.0, 0.0 );
  Point corner2( 0.0, 0.0, 0.0 );
  Point corner3( 0.0, 0.0, 0.0 );
  if ( peek( Token::left_brace ) )
    for ( ; ; )
    {
      if ( peek( "material" ) )
        material = parseMaterial();
      else if ( peek( "corner1" ) )
        corner1 = parsePoint();
      else if ( peek( "corner2" ) )
        corner2 = parsePoint();
      else if ( peek( "corner3" ) )
        corner3 = parsePoint();
      else if ( peek( Token::right_brace ) )
        break;
      else
        throwParseException( "Expected `material', `corner1', `corner2', `corner3' or }." );
    }
  return new Triangle( material, corner3, corner2, corner1 );
}

Object *Parser::parseObject()
{
	
    if ( peek( "group" ) )
        return parseGroupObject();
    else if ( peek( "griddedgroup" ) )
    	return parseGroupGriddedObject();
    else if ( peek( "plane" ) )
        return parsePlaneObject();
    else if ( peek( "sphere" ) )
        return parseSphereObject();
    else if ( peek( "spherepolar" ) )
        return parseSpherePolarObject();
    else if ( peek( "box" ) )
    	return parseBoxObject();
    else if ( peek( "disk" ) )
        	return parseDiskObject();
    else if ( peek( "ring" ) )
        	return parseRingObject();
    else if ( peek( "triangle" ) )
        	return parseTriangleObject();
    else if ( peek( "heightfield" ) )
    		return parseHeightfieldObject();
    else if ( next_token.token_type == Token::string )
    {
        map< string, Object * >::iterator found = defined_objects.find( parseString() );
        if ( found != defined_objects.end() )
            return ( *found ).second;
    }
    throwParseException( "Expected an object type." );
    return 0;
}

Sampler *Parser::parseSampler()
{
	//we expect the file to have <number of samples> <"jitter"/"uniform"> <"sbox"/"striangle">
	SampleType stype = SAMPLER_NONE;
	FilterType ftype = FILTER_NONE;
	int num_samples = parseInteger();
	std::string stype_str = parseString();
	std::string ftype_str = parseString();
    Sampler* s;
    
    if ( stype_str.compare( "uniform" ) == 0 )
        stype = SAMPLER_UNIFORM;
    else if ( stype_str.compare( "jitter" ) == 0 )
    	stype = SAMPLER_JITTER;
   	else if ( stype_str.compare( "jittered" ) == 0 )
    	stype = SAMPLER_JITTER;
    else {
    	throwParseException( "Expected uniform/jitter after integer in sampler declaration" );
    	return 0;
    }
    
    if ( ftype_str.compare( "sbox" ) == 0 )
        ftype = FILTER_SBOX;
    else if ( ftype_str.compare( "striangle" ) == 0 )
        ftype = FILTER_STRIANGLE;
    else if ( ftype_str.compare( "sphere" ) == 0 )
    	ftype = FILTER_SPHERE;
    else {
    	throwParseException( "Expected sbox/striangle/sphere after sample type. Instead found '" + ftype_str + "'" );
    	return 0;
    }
    if(stype == SAMPLER_UNIFORM)
    	return new SamplerUniform(num_samples, ftype);
    else if(stype == SAMPLER_JITTER)
    	return new SamplerJittered(num_samples, ftype);
    else {
    	cerr << "Wrong sampler type provided in scene file" << endl;
    	exit( 1 );
    }
    cerr << "Parsed the sampler." << endl;
    
}

Parser::Parser(
  istream &input )
  : input( input ),
    line_number( 1 ),
    column_number( 0 ),
    default_material( new LambertianMaterial( Color( 1.0, 1.0, 1.0 ), 0.6, 0.3 ) )
{
  readNextToken();
}

Scene *Parser::parseScene(
  string &filename )
{
  filename = "image.ppm";
  int xres = 512;
  int yres = 512;
  Scene *scene = new Scene();
  for ( ; ; ) {
    if ( peek( "filename" ) )
      filename = parseString();
    else if ( peek( "xres" ) )
      xres = parseInteger();
    else if ( peek( "yres" ) )
      yres = parseInteger();
    else if ( peek( "maxraydepth" ) )
      scene->setMaxRayDepth( parseInteger() );
    else if ( peek( "sampler" ) )
      scene->setSampler( parseSampler() );
    else if ( peek( "minattenuation" ) )
      scene->setMinAttenuation( parseReal() );
    else if ( peek( "camera" ) )
      scene->setCamera( parseCamera() );
    else if ( peek( "background" ) )
      scene->setBackground( parseBackground() );
    else if ( peek( "ambient" ) )
      scene->setAmbient( parseColor() );
    else if ( peek( "light" ) )
      scene->addLight( parseLight() );
    else if ( peek( "scene" ) )
      scene->setObject( parseObject() );
    else if ( peek( "define" ) ) {
      if ( peek( "material" ) ) {
        string name( parseString() );
        defined_materials.insert( pair< string, Material * >( name, parseMaterial() ) );
      } else if ( peek( "object" ) ) {
        string name( parseString() );
        defined_objects.insert( pair< string, Object * >( name, parseObject() ) );
      } else
        throwParseException( "Expected `material', or `object'" );
    }
    else if ( peek( Token::end_of_file ) )
        break;
    else
        throwParseException( "Expected `filename', `xres', `yres', `maxraydepth', `minattenuation', "
                             "`camera', `background', `ambient', `light', `scene', or `define'." );
  }
  scene->setImage( new Image( xres, yres ) );
  //ensure that even if we didn't parse a sampler/filter, we have one
  if(scene->getSampler() == 0) {
	  scene->setSampler(new SamplerUniform(1,  FILTER_SBOX));
  }
  return scene;
}
